package SQL.abysnSQL;

import java.util.HashMap;

import exceptions.DBTypeNotMatchException;

import heap.ResultRow;
import heap.ResultSchema;
import Common.Types;
import SQL.valueUtil.BoolExprVerify;
import SQL.valueUtil.ValueComparator;
import Transaction.Transaction;

/*
 * having-condition ::= having-value cop having-value
 | having-condition AND having-condition
 | having-condition OR having-condition
 | bool-expr
 */

public class HvCondition {
	public HvValue left, right;
	public HvCondition leftC, rightC;
	public Cop cop;
	public BoolCondition bool;
	public TYPE type;
	public int count = 0;
	public ConstValue l, r;
	public boolean flag;
	private HashMap<String, String> values = new HashMap<String, String>();

	public static enum TYPE {
		COP, AND, OR, BOOL
	}

	public HvCondition(HvValue l, Cop op, HvValue r) {
		left = l;
		right = r;
		cop = op;
		type = TYPE.COP;
	}

	public HvCondition(HvCondition l, HvCondition r, boolean b) {
		leftC = l;
		rightC = r;
		if (b) {
			type = TYPE.AND;
		} else
			type = TYPE.OR;
	}

	public HvCondition(BoolCondition b) {
		bool = b;
		type = TYPE.BOOL;
	}

	/**
	 * @param tmpSchema
	 * @return
	 * @throws DBTypeNotMatchException
	 */
	public boolean verify(ResultSchema schema) throws DBTypeNotMatchException {
		switch (type) {
		case AND:
			return leftC.verify(schema) && rightC.verify(schema);
		case OR:
			return leftC.verify(schema) || rightC.verify(schema);
		case BOOL:
			return bool.validation(schema);
		case COP:
			// l
			if (l == null) {
				String col = left.colname.toString();
				int i = getSchema(schema, col);
				switch (left.func.value) {
				case AVG: {
					float p = Float.parseFloat(values.get("AVG(" + col + ")"));
					p = p / count;
					l = new FloatValue(p);
					break;
				}
				case SUM: {
					float p = Float.parseFloat(values.get("SUM("+ col + ")"));
					l = new FloatValue(p);
					break;
				}
				case COUNT: {
					l = new IntegerValue(count);
					break;
				}
				case MIN: {
					switch (schema.get(i).type) {
					case Types.INT:
					case Types.BOOLEAN:
						l = new IntegerValue(Integer.parseInt(values.get("MIN("
								+ col + ")")));
					case Types.FLOAT:
						l = new FloatValue(Float.parseFloat(values.get("MIN("
								+ col + ")")));
					default:
						l = new StringValue(values.get("MIN(" + col + ")"));
					}
				}
				case MAX: {
					switch (schema.get(i).type) {
					case Types.INT:
					case Types.BOOLEAN:
						l = new IntegerValue(Integer.parseInt(values.get("MAX("
								+ col + ")")));
					case Types.FLOAT:
						l = new FloatValue(Float.parseFloat(values.get("MAX("
								+ col + ")")));
					default:
						l = new StringValue(values.get("MAX(" + col + ")"));
					}
				}
				}
			}
			// r
			if (r == null) {
				String col = right.colname.toString();
				int i = getSchema(schema, col);
				switch (right.func.value) {
				case AVG: {
					float p = Float.parseFloat(values.get("AVG(" + col + ")"));
					p = p / count;
					r = new FloatValue(p);
					break;
				}
				case SUM: {
					float p = Float.parseFloat(values.get("SUM(" + col + ")"));
					r = new FloatValue(p);
					break;
				}
				case COUNT: {
					r = new IntegerValue(count);
					break;
				}
				case MIN: {
					switch (schema.get(i).type) {
					case Types.INT:
					case Types.BOOLEAN:
						r = new IntegerValue(Integer.parseInt(values.get("MIN("
								+ col + ")")));
					case Types.FLOAT:
						r = new FloatValue(Float.parseFloat(values.get("MIN("
								+ col + ")")));
					default:
						r = new StringValue(values.get("MIN(" + col + ")"));
					}
				}
				case MAX: {
					switch (schema.get(i).type) {
					case Types.INT:
					case Types.BOOLEAN:
						r = new IntegerValue(Integer.parseInt(values.get("MAX("
								+ col + ")")));
					case Types.FLOAT:
						r = new FloatValue(Float.parseFloat(values.get("MAX("
								+ col + ")")));
					default:
						r = new StringValue(values.get("MAX(" + col + ")"));
					}
				}
				}
			}
			//
			if (l instanceof FloatValue || r instanceof FloatValue) {
				ValueComparator com = new ValueComparator();
				com.setLeft(l.floatValue());
				com.setRight(r.floatValue());
				com.cop = cop;
				values = new HashMap<String, String>();
				l = null;
				r = null;
				count = 0;
				flag = true;
				return com.compare();

			} else if (l instanceof IntegerValue && r instanceof IntegerValue) {
				ValueComparator com = new ValueComparator();
				com.setLeft(l.intValue());
				com.setRight(r.intValue());
				com.cop = cop;
				values = new HashMap<String, String>();
				l = null;
				r = null;
				count = 0;
				flag = true;
				return com.compare();
			} else {
				ValueComparator com = new ValueComparator();
				com.setLeft(l.stringValue());
				com.setRight(r.stringValue());
				com.cop = cop;
				values = new HashMap<String, String>();
				l = null;
				r = null;
				count = 0;
				flag = true;
				return com.compare();
			}
		}
		values = new HashMap<String, String>();
		l = null;
		r = null;
		count = 0;
		flag = true;
		return true;

	}

	/**
	 * @param currentScan
	 * @param tmpSchema
	 * @param db
	 * @param tx
	 * @throws Exception
	 */
	public void addTuple(ResultRow result, ResultSchema schema, String db,
			Transaction tx) throws Exception {
		count++;
		switch (type) {
		case AND:
		case OR:
			leftC.addTuple(result, schema, db, tx);
			rightC.addTuple(result, schema, db, tx);
			break;
		case BOOL:
			BoolExprVerify verift = new BoolExprVerify(bool, db, schema,
					result, tx);
			flag = verift.verify();
			break;
		case COP:
			if (left.type == HvValue.TYPE.VALUE) {
				l = left.value.getFinalValue(schema, result, db, tx);
			} else {
				String col = left.colname.toString();
				int i = getSchema(schema, col);
				switch (left.func.value) {
				case AVG: {
					String sum = values.get("AVG(" + col + ")");
					if (sum == null) {
						sum = result.get(i);
					} else {
						sum = Float.parseFloat(sum)
								+ Float.parseFloat(result.get(i)) + "";
					}
					values.put("AVG(" + col + ")", sum);
					break;
				}
				case SUM: {
					String sum = values.get("SUM(" + col + ")");
					if (sum == null) {
						sum = result.get(i);
					} else {
						sum = Float.parseFloat(sum)
								+ Float.parseFloat(result.get(i)) + "";
					}
					values.put("SUM(" + col + ")", sum);
					break;
				}
				case COUNT: {
					String count = values.get("COUNT(" + col + ")");
					if (count == null) {
						count = "1";
					} else {
						count = Integer.parseInt(count) + 1 + "";
					}
					values.put("COUNT(" + col + ")", count);
					break;
				}
				case MIN: {
					String tmp = values.get("MIN(" + col + ")");
					if (tmp == null) {
						tmp = result.get(i);
						values.put("MIN(" + col + ")", tmp);
					} else {
						switch (schema.get(i).type) {
						case Types.INT:
						case Types.BOOLEAN:
						case Types.FLOAT:
							float a = Float.parseFloat(tmp);
							float b = Float.parseFloat(result.get(i));
							if (b < a)
								values.put("MIN(" + col + ")", b + "");
							break;
						default:
							String now = result.get(i);
							if (now.compareTo(tmp) < 0)
								values.put("MIN(" + col + ")", now);

						}
					}

				}
				case MAX: {
					String tmp = values.get("MAX(" + col + ")");
					if (tmp == null) {
						tmp = result.get(i);
						values.put("MAX(" + col + ")", tmp);
					} else {
						switch (schema.get(i).type) {
						case Types.INT:
						case Types.BOOLEAN:
						case Types.FLOAT:
							float a = Float.parseFloat(tmp);
							float b = Float.parseFloat(result.get(i));
							if (b > a)
								values.put("MAX(" + col + ")", b + "");
							break;
						default:
							String now = result.get(i);
							if (now.compareTo(tmp) > 0)
								values.put("MAX(" + col + ")", now);

						}
					}
				}
				}

			}

			//
			if (right.type == HvValue.TYPE.VALUE) {
				r = right.value.getFinalValue(schema, result, db, tx);
			} else {
				String col = left.colname.toString();
				int i = getSchema(schema, col);
				switch (left.func.value) {
				case AVG: {
					String sum = values.get("AVG(" + col + ")");
					if (sum == null) {
						sum = result.get(i);
					} else {
						sum = Float.parseFloat(sum)
								+ Float.parseFloat(result.get(i)) + "";
					}
					values.put("AVG(" + col + ")", sum);
					break;
				}
				case SUM: {
					String sum = values.get("SUM(" + col + ")");
					if (sum == null) {
						sum = result.get(i);
					} else {
						sum = Float.parseFloat(sum)
								+ Float.parseFloat(result.get(i)) + "";
					}
					values.put("SUM(" + col + ")", sum);
					break;
				}
				case COUNT: {
					String count = values.get("COUNT(" + col + ")");
					if (count == null) {
						count = "1";
					} else {
						count = Integer.parseInt(count) + 1 + "";
					}
					values.put("COUNT(" + col + ")", count);
					break;
				}
				case MIN: {
					String tmp = values.get("MIN(" + col + ")");
					if (tmp == null) {
						tmp = result.get(i);
						values.put("MIN(" + col + ")", tmp);
					} else {
						switch (schema.get(i).type) {
						case Types.INT:
						case Types.BOOLEAN:
						case Types.FLOAT:
							float a = Float.parseFloat(tmp);
							float b = Float.parseFloat(result.get(i));
							if (b < a)
								values.put("MIN(" + col + ")", b + "");
							break;
						default:
							String now = result.get(i);
							if (now.compareTo(tmp) < 0)
								values.put("MIN(" + col + ")", now);

						}
					}

				}
				case MAX: {
					String tmp = values.get("MAX(" + col + ")");
					if (tmp == null) {
						tmp = result.get(i);
						values.put("MAX(" + col + ")", tmp);
					} else {
						switch (schema.get(i).type) {
						case Types.INT:
						case Types.BOOLEAN:
						case Types.FLOAT:
							float a = Float.parseFloat(tmp);
							float b = Float.parseFloat(result.get(i));
							if (b > a)
								values.put("MAX(" + col + ")", b + "");
							break;
						default:
							String now = result.get(i);
							if (now.compareTo(tmp) > 0)
								values.put("MAX(" + col + ")", now);

						}
					}
				}
				}

			}
		}
	}

	private int getSchema(ResultSchema schema, String name) {
		for (int i = 0; i < schema.size(); i++) {
			if (schema.get(i).name.endsWith("." + name)
					|| schema.get(i).name.equals(name)) {
				return i;
			}
		}
		return -1;
	}

}
